feat(deploy): seed the workspace vault password from the create payload - #129
Conversation
deploy_trigger reads <ws>/secrets/vault_pass.txt to set ANSIBLE_VAULT_PASSWORD_FILE and to unlock the workspace SSH keys (#112), but nothing wrote that file — it was an out-of-band operator step. So a deployment created through the API could not decrypt its vault, and the vault password the UI collects on the deploy form had nowhere to go. DeploymentCreate already carries a secrets map; take vault_password from it and write the file 0600, chmod'd before the content so the secret is never briefly world-readable. Absent or empty leaves the file alone, so an operator-seeded workspace is never clobbered. Tests cover the write, the mode, both no-op paths, and that the password is not echoed in the response.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb79bf5547
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Written before the content so the secret is never briefly world-readable. | ||
| vault_pass_file.touch(mode=0o600, exist_ok=True) | ||
| vault_pass_file.chmod(0o600) | ||
| vault_pass_file.write_text(payload.secrets["vault_password"]) |
There was a problem hiding this comment.
Reject duplicate workspaces before replacing their vault password
When a second POST uses the same codename and scenario label as an existing deployment, Workspace.create() reuses that deployment's directory and this write replaces its vault password before the database commit encounters the (codename, scenario_label) unique constraint in app/core/models.py:93. The request then fails, but the original deployment is left with a different password and can no longer decrypt its vault or SSH keys. Reserve or validate the deployment row before mutating the shared workspace, and avoid changing the file if persistence fails.
Useful? React with 👍 / 👎.
Backend half of range42-deployer-ui#91 — without it the UI's deploy form collects a vault password that goes nowhere.
deploy_triggerreads<ws>/secrets/vault_pass.txtto setANSIBLE_VAULT_PASSWORD_FILEand to unlock the workspace SSH keys (the #112 work depends on it). Nothing wrote that file — it was an out-of-band operator step, so a deployment created purely through the API could not decrypt its own vault.DeploymentCreatealready carries asecretsmap (it recognisesproxmox_token); this takesvault_passwordfrom the same place and writes the file with mode0600, chmod'd before the content so the secret is never briefly world-readable.Absent or empty leaves the file alone — an operator-seeded workspace must never be clobbered by a deploy that omitted the field.
Tests: the write, the mode, both no-op paths, and that the password is not echoed in the response.
469 passed, ruff clean.
Note the test harness reloads
app.core.workspaceas well as config — that module doesfrom app.core.config import settings, binding the object at import, so without the reload a second test in the same file writes into the previous test'stmp_path. Cost me a confusing failure; flagging it for whoever writes the next workspace test.